//
// @Author: zhengqi Wu
// @Description: 标识一个MIN网络标识
//  1. 身份标识为由单个名称组件组成的平面标识
// 	2. 内容标识为一到多个名称组件组成的层级化标识
// 	3. 包格式如下：
// 		Identifier = 101 TLV-LENGTH
//              *IdentifierComponent
// @Version: 1.0.0
// @Date: 2021/1/19 21:35
// @Copyright: MIN-Group；国家重大科技基础设施——未来网络北大实验室；深圳市信息论与未来网络重点实验室
//

const encoding = require('../encoding')
var {createIdentifierComponentByString,IdentifierComponent} = require('./IdentifierComponent')
var {TlvComponentBase} = require('./TlvComponentBase')
var aggregation = require('../common/aggregation')
const {createFragmentNumberIdentifierComponent} = require("./IdentifierComponent");
const {createVersionNumberIdentifierComponent} = require("./IdentifierComponent");
const {createIdentifierComponentByByteArray} = require("./IdentifierComponent");

//实现了多继承

class Identifier extends aggregation(encoding.SelfEncodingBase,TlvComponentBase){
    constructor() {
        super();
        this.components = [] // list<IdentifierComponent>
    }

    /**
     * @description 判断是否名称组件数量为空
     * @returns {boolean}
     */
    empty(){
        return this.components.length === 0
    }

    /**
     * @description 获取本标识包含的名称组件数量
     * @returns {number}
     */
    size(){
        return this.components.length
    }

    /**
     * @description 获取本标识包含的第 index 个名称组件
     * @param index
     * @returns {null|*}
     */
    get(index){
        if(index >= this.components.length){
            return null
        }
        return this.components[index]
    }

    /**
     * @description 获取标识中所有的名称组件
     * @returns {[]}
     */
    getComponents(){
        return this.components
    }

    /**
     * @description 获取前 num 个名称组件构成的标识
     * @param num
     * @returns {null|Identifier}
     */
    getPrefix(num){
        if(num > this.components.length){
            return null
        }
        return createIdentifierByComponents(this.components.slice(0, num))
    }

    /**
     * @description 返回从第 startIndex 个组件开始往后的 num 个组件组成的新标识
     * @param startIndex
     * @param num
     * @returns {null|Identifier}
     */
    getSubIdentifier(startIndex, num){
        if(startIndex + num > this.components.length){
            return null
        }
        return createIdentifierByComponents(this.components.slice(startIndex, startIndex + num))
    }

    /**
     * @description 判断当前标识是否有效
     * @returns {boolean}
     */
    isValid(){
        return this.components.length > 0
    }

    /**
     * @description 标识后面新增一个名称组件
     * @param component
     */
    append(component){
        this.components.push(component)
    }

    /**
     * AppendCommandParameters 在名字中添加命令参数字段
     * @param parameters {ControlParameters}
     */
    appendCommandParameters(parameters){
        try{
            let buffer = parameters.selfWireEncodeToBuffer(parameters)
            this.append(createIdentifierComponentByByteArray(buffer))
        }catch (e) {
            throw e
        }
    }

    /**
     * AppendVersionNumber 在名字后面添加一个版本号
     * @param versionNumber {Number | BigInt} 大于32位用BigInt 否则用Number
     */
    appendVersionNumber(versionNumber){
        try{
            this.append(createVersionNumberIdentifierComponent(versionNumber))
        }catch (e){
            throw e
        }
    }

    /**
     * AppendVersionNumber 在名字后面添加一个版本号
     * @param fragmentNumber {Number | BigInt} 大于32位用BigInt 否则用Number
     */
    appendFragmentNumber(fragmentNumber){
        try{
            this.append(createFragmentNumberIdentifierComponent(fragmentNumber))
        }catch (e){
            throw e
        }
    }

    /**
     * @description 对 Identifier 进行线速编码
     *  1. 需要注意，本函数只对 Identifier 进行编码，不包含外层区分不同标识的Type，编码的格式如下：
     *		Identifier = 101 TLV-LENGTH
     *             *IdentifierComponent
     * @param enc
     * @returns {number}
     */
    wireEncode(enc){
        let totalLength = 0
        try{
            // 编码 TLV-VALUE
            for(let i = this.components.length-1; i >= 0; i--){
                let component = this.components[i]
                let tmpLen = component.wireEncode(enc)
                totalLength += tmpLen
            }

            // for(let i = 0; i < this.components.length; i++){
            //     let component = this.components[i]
            //     let tmpLen = component.wireEncode(enc)
            //     totalLength += tmpLen
            // }

            // 编码 TLV-Length
            let tmpLen = enc.prependVarNumber((totalLength))
            totalLength += tmpLen

            // 编码 TLV-TYPE
            tmpLen = enc.prependVarNumber((encoding.tlv.ComponentType.TlvIdentifier))
            totalLength += tmpLen

            return totalLength
        }catch (e){
            throw e
        }

    }

    /**
     * @description 将 TLV Block 进行线速解码为一个 Identifier
     * @param block
     */
    wireDecode(block){
        // 检查 Type 是否正确
        try{
            if(encoding.tlv.expectType(block.getType(), (encoding.tlv.ComponentType.TlvIdentifier))){
                // 读取 freshnessPeriod
                // getValue => vlint
                block.parseSubElements()
                let elements = block.getSubElements()

                // 解析子 TLV
                for(let i = 0; i < elements.length; i++){
                    let element = elements[i]
                    let tmpIdentifierComponent = new IdentifierComponent()
                    tmpIdentifierComponent.wireDecode(element)
                    this.append(tmpIdentifierComponent)
                }
            }
        }catch (e){
            throw e
        }

    }

    /**
     * @description 将Identifier表示成Uri字符串
     * @returns {string}
     */
    toUri(){
        let uri = ""
        for(let i = 0; i < this.components.length; i++){
            let component = this.components[i]
            uri += "/"
            uri += component.toUri()
        }
        return uri
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////
    ///// 初始化函数 Build*
    /////////////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * @description 根据一个 TLV Block 初始化当前Identifier
     * @param block
     */
    buildIdentifierByBlock(block){
        // 如果不存在子 TLV， 则不可能是一个 Identifier
        try {
            block.parseSubElements()
            if(block.getSubElements().length >= 1){
                this.wireDecode(block)
            }else {
                throw new IdentifierError("Invalid block while parse it to Identifier")
            }
        }catch (e) {
            throw e
        }
    }

    /**
     * @description 根据标识字符串初始化当前Identifier
     * @param string
     */
    buildIdentifierByString(string){
        let components = []
        try{
            if(string.length > 0 && string[0] === '/'){
                let componentStrings = string.split('/')
                for(let i = 1; i < componentStrings.length; i++){
                    let tmp = createIdentifierComponentByString(componentStrings[i])
                    components.push(tmp)
                }
                this.buildIdentifierByComponents(components)
            }else{
                throw new IdentifierError(`Invalid identifierString ${string}`)
            }
        }catch (e){
            throw e
        }
    }

    /**
     * @description 根据 components 初始化当前 Identifier
     * @param containers
     */
    buildIdentifierByComponents(containers){
        try{
            if(containers.length > 0){
                for(let i = 0; i < containers.length; i++){
                    let component = containers[i]
                    // this.append(component) golang写法
                    this.components.push(component)
                }
            }else{
                throw new IdentifierError("Component array not allow empty while construct Identifer")
            }
        }catch (e){
            throw e
        }
    }
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////
///// 构造函数 Create*
/////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * @description 根据一个 TLV Block 创建一个Identifier
 * @param block
 * @returns {Identifier}
 */
function createIdentifierByBlock(block){
    try{
        let identifier = new Identifier()
        identifier.buildIdentifierByBlock(block)
        return identifier
    }catch (e){
        throw e
    }

}

/**
 * @description 根据标识的字符串标识创建一个标识
 * @param string
 * @returns {Identifier}
 */
function createIdentifierByString(string){
    try{
        let identifier = new Identifier()
        identifier.buildIdentifierByString(string)
        return identifier
    }catch (e){
        throw e
    }

}

/**
 * @description 根据components构造一个Identifier
 * @param container
 * @returns {Identifier}
 */
function createIdentifierByComponents(container){
    try{
        let identifier = new Identifier()
        identifier.buildIdentifierByComponents(container)
        return identifier
    }catch (e){
        throw e
    }
}

class IdentifierError extends Error{
    constructor(m){
        super(m)
        this.name = 'BlockError'
    }

    error(){
        console.log(`{this.name}: {this.message}`)
    }
}

module.exports = {
    Identifier: Identifier,
    createIdentifierByBlock: createIdentifierByBlock,
    createIdentifierByString: createIdentifierByString,
    createIdentifierByComponents: createIdentifierByComponents
}